From 923ce72409f184bd8e8c61b196260891036ba87e Mon Sep 17 00:00:00 2001
From: Antony Lee <anntzer.lee@gmail.com>
Date: Thu, 30 Aug 2018 15:27:55 +0200
Subject: [PATCH] Simplify version checks for freetype and libpng.

Currently, setupext.py replicates a lot of work done by the compiler to
check whether header files are present, and whether freetype and libpng
have sufficiently recent versions.

Instead, we can just add a small stub source file at the top of the
extension sources which just tries to include the header and checks the
version macros.  If the header is not found, compilation will
immediately abort with `foo.h: No such file or directory`; if the
version is too old, we can emit an appropriate error message (`#pragma
message` is supported by all major compilers and allows expanding of
macros in the error message).

[Retrieved from:
https://github.com/matplotlib/matplotlib/commit/d1060a885309ec7ac19ca912d3011a5eb1673bd5]
Signed-off-by: Fabrice Fontaine <fontaine.fabrice@gmail.com>
---
 setupext.py              | 83 +++++-----------------------------------
 src/checkdep_freetype2.c | 13 +++++++
 src/checkdep_libpng.c    |  5 +++
 3 files changed, 28 insertions(+), 73 deletions(-)
 create mode 100644 src/checkdep_freetype2.c
 create mode 100644 src/checkdep_libpng.c

diff --git a/setupext.py b/setupext.py
index d5f4b81f562..a5163e39288 100644
--- a/setupext.py
+++ b/setupext.py
@@ -814,6 +814,13 @@ def add_flags(self, ext, add_sources=True):
                                for x in agg_sources)
 
 
+# For FreeType2 and libpng, we add a separate checkdep_foo.c source to at the
+# top of the extension sources.  This file is compiled first and immediately
+# aborts the compilation either with "foo.h: No such file or directory" if the
+# header is not found, or an appropriate error message if the header indicates
+# a too-old version.
+
+
 class FreeType(SetupPackage):
     name = "freetype"
     pkg_names = {
@@ -825,59 +832,8 @@ class FreeType(SetupPackage):
         "windows_url": "http://gnuwin32.sourceforge.net/packages/freetype.htm"
         }
 
-    def check(self):
-        if options.get('local_freetype'):
-            return "Using local version for testing"
-
-        if sys.platform == 'win32':
-            try:
-                check_include_file(get_include_dirs(), 'ft2build.h', 'freetype')
-            except CheckFailed:
-                check_include_file(get_include_dirs(), os.path.join('freetype2', 'ft2build.h'), 'freetype')
-            return 'Using unknown version found on system.'
-
-        status, output = subprocess.getstatusoutput(
-            "freetype-config --ftversion")
-        if status == 0:
-            version = output
-        else:
-            version = None
-
-        # Early versions of freetype grep badly inside freetype-config,
-        # so catch those cases. (tested with 2.5.3).
-        if version is None or 'No such file or directory\ngrep:' in version:
-            version = self.version_from_header()
-
-        # pkg_config returns the libtool version rather than the
-        # freetype version so we need to explicitly pass the version
-        # to _check_for_pkg_config
-        return self._check_for_pkg_config(
-            'freetype2', 'ft2build.h',
-            min_version='2.3', version=version)
-
-    def version_from_header(self):
-        version = 'unknown'
-        ext = self.get_extension()
-        if ext is None:
-            return version
-        # Return the first version found in the include dirs.
-        for include_dir in ext.include_dirs:
-            header_fname = os.path.join(include_dir, 'freetype.h')
-            if os.path.exists(header_fname):
-                major, minor, patch = 0, 0, 0
-                with open(header_fname, 'r') as fh:
-                    for line in fh:
-                        if line.startswith('#define FREETYPE_'):
-                            value = line.rsplit(' ', 1)[1].strip()
-                            if 'MAJOR' in line:
-                                major = value
-                            elif 'MINOR' in line:
-                                minor = value
-                            else:
-                                patch = value
-                return '.'.join([major, minor, patch])
-
     def add_flags(self, ext):
+        ext.sources.insert(0, 'src/checkdep_freetype2.c')
         if options.get('local_freetype'):
             src_path = os.path.join(
                 'build', 'freetype-{0}'.format(LOCAL_FREETYPE_VERSION))
@@ -1058,30 +1014,11 @@ class Png(SetupPackage):
         "windows_url": "http://gnuwin32.sourceforge.net/packages/libpng.htm"
         }
 
-    def check(self):
-        if sys.platform == 'win32':
-            check_include_file(get_include_dirs(), 'png.h', 'png')
-            return 'Using unknown version found on system.'
-
-        status, output = subprocess.getstatusoutput("libpng-config --version")
-        if status == 0:
-            version = output
-        else:
-            version = None
-
-        try:
-            return self._check_for_pkg_config(
-                'libpng', 'png.h',
-                min_version='1.2', version=version)
-        except CheckFailed as e:
-            if has_include_file(get_include_dirs(), 'png.h'):
-                return str(e) + ' Using unknown version found on system.'
-            raise
-
     def get_extension(self):
         sources = [
+            'src/checkdep_libpng.c',
             'src/_png.cpp',
-            'src/mplutils.cpp'
+            'src/mplutils.cpp',
             ]
         ext = make_extension('matplotlib._png', sources)
         pkg_config.setup_extension(
diff --git a/src/checkdep_freetype2.c b/src/checkdep_freetype2.c
new file mode 100644
index 00000000000..bf9a8c94e38
--- /dev/null
+++ b/src/checkdep_freetype2.c
@@ -0,0 +1,13 @@
+#include <ft2build.h>
+#include FT_FREETYPE_H
+
+#define XSTR(x) STR(x)
+#define STR(x) #x
+
+#pragma message("Compiling with FreeType version " \
+  XSTR(FREETYPE_MAJOR) "." XSTR(FREETYPE_MINOR) "." XSTR(FREETYPE_PATCH) ".")
+#if FREETYPE_MAJOR << 16 + FREETYPE_MINOR << 8 + FREETYPE_PATCH < 0x020300
+    #error "FreeType version 2.3 or higher is required." \
+      "Consider setting the MPLLOCALFREETYPE environment variable to 1."
+  #error
+#endif
diff --git a/src/checkdep_libpng.c b/src/checkdep_libpng.c
new file mode 100644
index 00000000000..5ebe5cbe4d7
--- /dev/null
+++ b/src/checkdep_libpng.c
@@ -0,0 +1,5 @@
+#include <png.h>
+#pragma message("Compiling with libpng version " PNG_LIBPNG_VER_STRING ".")
+#if PNG_LIBPNG_VER < 10200
+  #error "libpng version 1.2 or higher is required."
+#endif
